package com.tul.exercise.base.java8.stream.reduce;

import com.tul.exercise.base.java8.stream.base.Apple;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * reduce操作
 *
 * @author tuliang
 */
public class ReduceTest {
    @Test
    @DisplayName("reduce实现求和")
    public void test01() {

        // 求和
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);
        int sum = Stream.of(1, 2, 3, 4, 5).reduce(0, (a, b) -> a + b);
        // 函数引用
        int sum2 = Stream.of(1, 2, 3, 4, 5).reduce(0, Integer::sum);
        Assertions.assertEquals(sum, sum2);


        List<Apple> appleList = Apple.createApples(100);
        // 迭代加速器
        BiFunction<Double, Apple, Double> acc = (Double initValue, Apple apple) -> apple.getWeight() + initValue;
        // 累加函数
        BinaryOperator<Double> add = (x, y) -> x + y;

        // 函数引用
        Double sum3 = appleList.stream().reduce(0.0, acc, add);

        // 先转为double流再计算总量
        Double sum4 = appleList.stream().map(apple -> apple.getWeight()).collect(Collectors.toList())
                .stream().reduce(0.0, Double::sum);
        Assertions.assertEquals(sum3, sum4);

        // 直接使用lambda表达式
        Double sum5 = appleList.stream().reduce(0.0,
                (Double initValue, Apple apple) -> apple.getWeight() + initValue,
                (x, y) -> x + y);
        Assertions.assertEquals(sum3, sum5);
    }

    @Test
    @DisplayName("reduce模拟toList操作")
    public void test02() {

        List<Apple> apples = Apple.createApples(9);

        BiFunction<List<Apple>, Apple, List<Apple>> acc = (list, item) -> {
            list.add(item);
            return list;
        };

        BinaryOperator<List<Apple>> combiner = (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };

        List<Apple> apples1 = apples.stream().reduce(new ArrayList<>(), acc, combiner);
        System.out.println(apples1);
    }

    @Test
    @DisplayName("reduce模拟map操作")
    public void test03() {
        List<Apple> apples = Apple.createApples(10);

        BiFunction<List<String>, Apple, List<String>> acc = (list, apple) -> {
            list.add(apple.getName());
            return list;
        };

        BinaryOperator<List<String>> combiner = (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };

        List<String> nameList = apples.stream().reduce(new ArrayList<>(), acc, combiner);
        System.out.println(nameList);
    }

    @Test
    @DisplayName("reduce模拟filter操作")
    public void test04() {
        List<Apple> apples = Apple.createApples(9);

        Predicate<Apple> isHeaveApple = apple -> apple.getWeight() > 5000;
        BiFunction<List<Apple>, Apple, List<Apple>> acc = (list, item) -> {
            if (isHeaveApple.test(item)) {
                list.add(item);
            }
            return list;
        };

        BinaryOperator<List<Apple>> combiner = (list1, list2) -> {
            list1.addAll(list2);
            return list1;
        };

        List<Apple> apples1 = apples.stream().reduce(new ArrayList<>(), acc, combiner);
        System.out.println(apples1);
    }
}
